{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "f04dd603-a2d1-48ce-8c17-9f1dba8de1ee",
   "metadata": {},
   "source": [
    "Chapter 10\n",
    "\n",
    "# 使用scikit-learn完成一元线性回归\n",
    "《线性代数》 | 鸢尾花书：数学不难"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ccafb456-2453-4c82-8a65-b1963a370cb2",
   "metadata": {},
   "source": [
    "这段代码从数学上完整演示了**一元线性回归（Univariate Linear Regression）** 的建模过程，包括参数拟合、预测、误差计算与可视化。它使用的是 Scikit-learn 提供的 `LinearRegression` 工具，自动完成了模型训练与预测步骤，背后的数学原理与最小二乘法完全一致。\n",
    "\n",
    "---\n",
    "\n",
    "从数学角度看，我们的目标是：给定一组输入输出数据点 $(x_i, y_i)$，$i = 1, \\dots, n$，我们假设它们之间存在线性关系：\n",
    "\n",
    "$$\n",
    "\\hat{y}_i = b_0 + b_1 x_i\n",
    "$$\n",
    "\n",
    "其中 $b_0$ 是截距项，$b_1$ 是斜率。用向量和矩阵记法，可以把所有数据点的预测值写成一个向量形式：\n",
    "\n",
    "$$\n",
    "\\hat{\\boldsymbol{y}} = X \\boldsymbol{b}\n",
    "$$\n",
    "\n",
    "这里的 $X$ 是 $n \\times 2$ 的**设计矩阵**，第一列为全1（对应 $b_0$），第二列为自变量 $x_i$：\n",
    "\n",
    "$$\n",
    "X =\n",
    "\\begin{bmatrix}\n",
    "1 & x_1 \\\\\n",
    "1 & x_2 \\\\\n",
    "\\vdots & \\vdots \\\\\n",
    "1 & x_n\n",
    "\\end{bmatrix}\n",
    "$$\n",
    "\n",
    "参数向量 $\\boldsymbol{b}$ 包括截距和斜率：\n",
    "\n",
    "$$\n",
    "\\boldsymbol{b} =\n",
    "\\begin{bmatrix}\n",
    "b_0 \\\\\n",
    "b_1\n",
    "\\end{bmatrix}\n",
    "$$\n",
    "\n",
    "---\n",
    "\n",
    "通过最小化预测值与真实值之间的残差平方和，即损失函数：\n",
    "\n",
    "$$\n",
    "J(\\boldsymbol{b}) = \\|\\boldsymbol{y} - X \\boldsymbol{b}\\|_2^2 = \\sum_{i=1}^n (y_i - \\hat{y}_i)^2\n",
    "$$\n",
    "\n",
    "我们可以推导出一个封闭形式的最优解：\n",
    "\n",
    "$$\n",
    "\\boldsymbol{b} = (X^\\top X)^{-1} X^\\top \\boldsymbol{y}\n",
    "$$\n",
    "\n",
    "在这段代码中，这一过程被 `model.fit(x, y)` 内部自动完成。此时 `x` 只有一列，所以 scikit-learn 会自动添加常数项并计算出参数 $b_0$ 与 $b_1$，它们分别可以通过 `model.intercept_` 和 `model.coef_` 得到。\n",
    "\n",
    "---\n",
    "\n",
    "完成拟合后，我们使用模型在原始训练数据上进行预测：\n",
    "\n",
    "$$\n",
    "\\hat{\\boldsymbol{y}} = X \\boldsymbol{b}\n",
    "$$\n",
    "\n",
    "这在代码中通过 `model.predict(x)` 得到预测向量 `y_pred`。\n",
    "\n",
    "接着，代码生成了新的输入值 $x \\in [0, 10]$ 上的预测结果 `y_array_pred`，用来绘制平滑的回归直线：\n",
    "\n",
    "$$\n",
    "\\hat{y}_{\\text{new}} = b_0 + b_1 x_{\\text{new}}\n",
    "$$\n",
    "\n",
    "---\n",
    "\n",
    "随后，我们计算预测误差：\n",
    "\n",
    "$$\n",
    "\\boldsymbol{e} = \\boldsymbol{y} - \\hat{\\boldsymbol{y}}\n",
    "$$\n",
    "\n",
    "并进一步计算它的 L2 范数的平方：\n",
    "\n",
    "$$\n",
    "\\|\\boldsymbol{e}\\|_2^2 = \\boldsymbol{e}^\\top \\boldsymbol{e}\n",
    "$$\n",
    "\n",
    "这表示我们拟合结果的总残差，是衡量模型好坏的重要指标。\n",
    "\n",
    "---\n",
    "\n",
    "最后，代码用可视化方式展示了整个回归过程：\n",
    "\n",
    "- 用蓝色叉点 (`x`) 显示原始观测点 $(x_i, y_i)$；\n",
    "- 用红色叉点 (`x`) 显示预测值 $(x_i, \\hat{y}_i)$；\n",
    "- 用红色线段画出拟合直线 $\\hat{y} = b_0 + b_1 x$；\n",
    "- 用橙色半透明线段连接每个预测值与真实值，表示残差（预测误差）；\n",
    "- 图形范围固定在 $[0, 10] \\times [0, 10]$，并设置了网格与坐标轴标签。\n",
    "\n",
    "---\n",
    "\n",
    "整体来看，这段代码完整实现了线性回归建模的每个步骤，并且在图形上明确展现了拟合质量与误差，是最小二乘思想的直观体现。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "671ca3f5-e6e1-493e-acb5-ef6870526c0b",
   "metadata": {},
   "source": [
    "## 初始化"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "8fe3b786-a3d8-44fb-9532-31f3f9071c0d",
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "import matplotlib.pyplot as plt\n",
    "from sklearn.linear_model import LinearRegression"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7c68d4ab-0446-4adb-8c05-3bdeccbf9162",
   "metadata": {},
   "source": [
    "## 数据"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "edc6cd6b-ecc0-4111-a803-358dffb2cb1d",
   "metadata": {},
   "outputs": [],
   "source": [
    "x = np.array([1,2,3,5,6,7,8,9]).reshape(-1,1)   # 自变量 x\n",
    "y = np.array([3,1,4,6,5,8,7,8]).reshape(-1,1)   # 因变量 y"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3a4e6365-f805-4a65-878a-9e6858c551ad",
   "metadata": {},
   "source": [
    "## 建立线性回归模型"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "91fc848e-01f9-40a9-806c-888f825cb72e",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<style>#sk-container-id-1 {\n",
       "  /* Definition of color scheme common for light and dark mode */\n",
       "  --sklearn-color-text: black;\n",
       "  --sklearn-color-line: gray;\n",
       "  /* Definition of color scheme for unfitted estimators */\n",
       "  --sklearn-color-unfitted-level-0: #fff5e6;\n",
       "  --sklearn-color-unfitted-level-1: #f6e4d2;\n",
       "  --sklearn-color-unfitted-level-2: #ffe0b3;\n",
       "  --sklearn-color-unfitted-level-3: chocolate;\n",
       "  /* Definition of color scheme for fitted estimators */\n",
       "  --sklearn-color-fitted-level-0: #f0f8ff;\n",
       "  --sklearn-color-fitted-level-1: #d4ebff;\n",
       "  --sklearn-color-fitted-level-2: #b3dbfd;\n",
       "  --sklearn-color-fitted-level-3: cornflowerblue;\n",
       "\n",
       "  /* Specific color for light theme */\n",
       "  --sklearn-color-text-on-default-background: var(--sg-text-color, var(--theme-code-foreground, var(--jp-content-font-color1, black)));\n",
       "  --sklearn-color-background: var(--sg-background-color, var(--theme-background, var(--jp-layout-color0, white)));\n",
       "  --sklearn-color-border-box: var(--sg-text-color, var(--theme-code-foreground, var(--jp-content-font-color1, black)));\n",
       "  --sklearn-color-icon: #696969;\n",
       "\n",
       "  @media (prefers-color-scheme: dark) {\n",
       "    /* Redefinition of color scheme for dark theme */\n",
       "    --sklearn-color-text-on-default-background: var(--sg-text-color, var(--theme-code-foreground, var(--jp-content-font-color1, white)));\n",
       "    --sklearn-color-background: var(--sg-background-color, var(--theme-background, var(--jp-layout-color0, #111)));\n",
       "    --sklearn-color-border-box: var(--sg-text-color, var(--theme-code-foreground, var(--jp-content-font-color1, white)));\n",
       "    --sklearn-color-icon: #878787;\n",
       "  }\n",
       "}\n",
       "\n",
       "#sk-container-id-1 {\n",
       "  color: var(--sklearn-color-text);\n",
       "}\n",
       "\n",
       "#sk-container-id-1 pre {\n",
       "  padding: 0;\n",
       "}\n",
       "\n",
       "#sk-container-id-1 input.sk-hidden--visually {\n",
       "  border: 0;\n",
       "  clip: rect(1px 1px 1px 1px);\n",
       "  clip: rect(1px, 1px, 1px, 1px);\n",
       "  height: 1px;\n",
       "  margin: -1px;\n",
       "  overflow: hidden;\n",
       "  padding: 0;\n",
       "  position: absolute;\n",
       "  width: 1px;\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-dashed-wrapped {\n",
       "  border: 1px dashed var(--sklearn-color-line);\n",
       "  margin: 0 0.4em 0.5em 0.4em;\n",
       "  box-sizing: border-box;\n",
       "  padding-bottom: 0.4em;\n",
       "  background-color: var(--sklearn-color-background);\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-container {\n",
       "  /* jupyter's `normalize.less` sets `[hidden] { display: none; }`\n",
       "     but bootstrap.min.css set `[hidden] { display: none !important; }`\n",
       "     so we also need the `!important` here to be able to override the\n",
       "     default hidden behavior on the sphinx rendered scikit-learn.org.\n",
       "     See: https://github.com/scikit-learn/scikit-learn/issues/21755 */\n",
       "  display: inline-block !important;\n",
       "  position: relative;\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-text-repr-fallback {\n",
       "  display: none;\n",
       "}\n",
       "\n",
       "div.sk-parallel-item,\n",
       "div.sk-serial,\n",
       "div.sk-item {\n",
       "  /* draw centered vertical line to link estimators */\n",
       "  background-image: linear-gradient(var(--sklearn-color-text-on-default-background), var(--sklearn-color-text-on-default-background));\n",
       "  background-size: 2px 100%;\n",
       "  background-repeat: no-repeat;\n",
       "  background-position: center center;\n",
       "}\n",
       "\n",
       "/* Parallel-specific style estimator block */\n",
       "\n",
       "#sk-container-id-1 div.sk-parallel-item::after {\n",
       "  content: \"\";\n",
       "  width: 100%;\n",
       "  border-bottom: 2px solid var(--sklearn-color-text-on-default-background);\n",
       "  flex-grow: 1;\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-parallel {\n",
       "  display: flex;\n",
       "  align-items: stretch;\n",
       "  justify-content: center;\n",
       "  background-color: var(--sklearn-color-background);\n",
       "  position: relative;\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-parallel-item {\n",
       "  display: flex;\n",
       "  flex-direction: column;\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-parallel-item:first-child::after {\n",
       "  align-self: flex-end;\n",
       "  width: 50%;\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-parallel-item:last-child::after {\n",
       "  align-self: flex-start;\n",
       "  width: 50%;\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-parallel-item:only-child::after {\n",
       "  width: 0;\n",
       "}\n",
       "\n",
       "/* Serial-specific style estimator block */\n",
       "\n",
       "#sk-container-id-1 div.sk-serial {\n",
       "  display: flex;\n",
       "  flex-direction: column;\n",
       "  align-items: center;\n",
       "  background-color: var(--sklearn-color-background);\n",
       "  padding-right: 1em;\n",
       "  padding-left: 1em;\n",
       "}\n",
       "\n",
       "\n",
       "/* Toggleable style: style used for estimator/Pipeline/ColumnTransformer box that is\n",
       "clickable and can be expanded/collapsed.\n",
       "- Pipeline and ColumnTransformer use this feature and define the default style\n",
       "- Estimators will overwrite some part of the style using the `sk-estimator` class\n",
       "*/\n",
       "\n",
       "/* Pipeline and ColumnTransformer style (default) */\n",
       "\n",
       "#sk-container-id-1 div.sk-toggleable {\n",
       "  /* Default theme specific background. It is overwritten whether we have a\n",
       "  specific estimator or a Pipeline/ColumnTransformer */\n",
       "  background-color: var(--sklearn-color-background);\n",
       "}\n",
       "\n",
       "/* Toggleable label */\n",
       "#sk-container-id-1 label.sk-toggleable__label {\n",
       "  cursor: pointer;\n",
       "  display: block;\n",
       "  width: 100%;\n",
       "  margin-bottom: 0;\n",
       "  padding: 0.5em;\n",
       "  box-sizing: border-box;\n",
       "  text-align: center;\n",
       "}\n",
       "\n",
       "#sk-container-id-1 label.sk-toggleable__label-arrow:before {\n",
       "  /* Arrow on the left of the label */\n",
       "  content: \"▸\";\n",
       "  float: left;\n",
       "  margin-right: 0.25em;\n",
       "  color: var(--sklearn-color-icon);\n",
       "}\n",
       "\n",
       "#sk-container-id-1 label.sk-toggleable__label-arrow:hover:before {\n",
       "  color: var(--sklearn-color-text);\n",
       "}\n",
       "\n",
       "/* Toggleable content - dropdown */\n",
       "\n",
       "#sk-container-id-1 div.sk-toggleable__content {\n",
       "  max-height: 0;\n",
       "  max-width: 0;\n",
       "  overflow: hidden;\n",
       "  text-align: left;\n",
       "  /* unfitted */\n",
       "  background-color: var(--sklearn-color-unfitted-level-0);\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-toggleable__content.fitted {\n",
       "  /* fitted */\n",
       "  background-color: var(--sklearn-color-fitted-level-0);\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-toggleable__content pre {\n",
       "  margin: 0.2em;\n",
       "  border-radius: 0.25em;\n",
       "  color: var(--sklearn-color-text);\n",
       "  /* unfitted */\n",
       "  background-color: var(--sklearn-color-unfitted-level-0);\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-toggleable__content.fitted pre {\n",
       "  /* unfitted */\n",
       "  background-color: var(--sklearn-color-fitted-level-0);\n",
       "}\n",
       "\n",
       "#sk-container-id-1 input.sk-toggleable__control:checked~div.sk-toggleable__content {\n",
       "  /* Expand drop-down */\n",
       "  max-height: 200px;\n",
       "  max-width: 100%;\n",
       "  overflow: auto;\n",
       "}\n",
       "\n",
       "#sk-container-id-1 input.sk-toggleable__control:checked~label.sk-toggleable__label-arrow:before {\n",
       "  content: \"▾\";\n",
       "}\n",
       "\n",
       "/* Pipeline/ColumnTransformer-specific style */\n",
       "\n",
       "#sk-container-id-1 div.sk-label input.sk-toggleable__control:checked~label.sk-toggleable__label {\n",
       "  color: var(--sklearn-color-text);\n",
       "  background-color: var(--sklearn-color-unfitted-level-2);\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-label.fitted input.sk-toggleable__control:checked~label.sk-toggleable__label {\n",
       "  background-color: var(--sklearn-color-fitted-level-2);\n",
       "}\n",
       "\n",
       "/* Estimator-specific style */\n",
       "\n",
       "/* Colorize estimator box */\n",
       "#sk-container-id-1 div.sk-estimator input.sk-toggleable__control:checked~label.sk-toggleable__label {\n",
       "  /* unfitted */\n",
       "  background-color: var(--sklearn-color-unfitted-level-2);\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-estimator.fitted input.sk-toggleable__control:checked~label.sk-toggleable__label {\n",
       "  /* fitted */\n",
       "  background-color: var(--sklearn-color-fitted-level-2);\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-label label.sk-toggleable__label,\n",
       "#sk-container-id-1 div.sk-label label {\n",
       "  /* The background is the default theme color */\n",
       "  color: var(--sklearn-color-text-on-default-background);\n",
       "}\n",
       "\n",
       "/* On hover, darken the color of the background */\n",
       "#sk-container-id-1 div.sk-label:hover label.sk-toggleable__label {\n",
       "  color: var(--sklearn-color-text);\n",
       "  background-color: var(--sklearn-color-unfitted-level-2);\n",
       "}\n",
       "\n",
       "/* Label box, darken color on hover, fitted */\n",
       "#sk-container-id-1 div.sk-label.fitted:hover label.sk-toggleable__label.fitted {\n",
       "  color: var(--sklearn-color-text);\n",
       "  background-color: var(--sklearn-color-fitted-level-2);\n",
       "}\n",
       "\n",
       "/* Estimator label */\n",
       "\n",
       "#sk-container-id-1 div.sk-label label {\n",
       "  font-family: monospace;\n",
       "  font-weight: bold;\n",
       "  display: inline-block;\n",
       "  line-height: 1.2em;\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-label-container {\n",
       "  text-align: center;\n",
       "}\n",
       "\n",
       "/* Estimator-specific */\n",
       "#sk-container-id-1 div.sk-estimator {\n",
       "  font-family: monospace;\n",
       "  border: 1px dotted var(--sklearn-color-border-box);\n",
       "  border-radius: 0.25em;\n",
       "  box-sizing: border-box;\n",
       "  margin-bottom: 0.5em;\n",
       "  /* unfitted */\n",
       "  background-color: var(--sklearn-color-unfitted-level-0);\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-estimator.fitted {\n",
       "  /* fitted */\n",
       "  background-color: var(--sklearn-color-fitted-level-0);\n",
       "}\n",
       "\n",
       "/* on hover */\n",
       "#sk-container-id-1 div.sk-estimator:hover {\n",
       "  /* unfitted */\n",
       "  background-color: var(--sklearn-color-unfitted-level-2);\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-estimator.fitted:hover {\n",
       "  /* fitted */\n",
       "  background-color: var(--sklearn-color-fitted-level-2);\n",
       "}\n",
       "\n",
       "/* Specification for estimator info (e.g. \"i\" and \"?\") */\n",
       "\n",
       "/* Common style for \"i\" and \"?\" */\n",
       "\n",
       ".sk-estimator-doc-link,\n",
       "a:link.sk-estimator-doc-link,\n",
       "a:visited.sk-estimator-doc-link {\n",
       "  float: right;\n",
       "  font-size: smaller;\n",
       "  line-height: 1em;\n",
       "  font-family: monospace;\n",
       "  background-color: var(--sklearn-color-background);\n",
       "  border-radius: 1em;\n",
       "  height: 1em;\n",
       "  width: 1em;\n",
       "  text-decoration: none !important;\n",
       "  margin-left: 1ex;\n",
       "  /* unfitted */\n",
       "  border: var(--sklearn-color-unfitted-level-1) 1pt solid;\n",
       "  color: var(--sklearn-color-unfitted-level-1);\n",
       "}\n",
       "\n",
       ".sk-estimator-doc-link.fitted,\n",
       "a:link.sk-estimator-doc-link.fitted,\n",
       "a:visited.sk-estimator-doc-link.fitted {\n",
       "  /* fitted */\n",
       "  border: var(--sklearn-color-fitted-level-1) 1pt solid;\n",
       "  color: var(--sklearn-color-fitted-level-1);\n",
       "}\n",
       "\n",
       "/* On hover */\n",
       "div.sk-estimator:hover .sk-estimator-doc-link:hover,\n",
       ".sk-estimator-doc-link:hover,\n",
       "div.sk-label-container:hover .sk-estimator-doc-link:hover,\n",
       ".sk-estimator-doc-link:hover {\n",
       "  /* unfitted */\n",
       "  background-color: var(--sklearn-color-unfitted-level-3);\n",
       "  color: var(--sklearn-color-background);\n",
       "  text-decoration: none;\n",
       "}\n",
       "\n",
       "div.sk-estimator.fitted:hover .sk-estimator-doc-link.fitted:hover,\n",
       ".sk-estimator-doc-link.fitted:hover,\n",
       "div.sk-label-container:hover .sk-estimator-doc-link.fitted:hover,\n",
       ".sk-estimator-doc-link.fitted:hover {\n",
       "  /* fitted */\n",
       "  background-color: var(--sklearn-color-fitted-level-3);\n",
       "  color: var(--sklearn-color-background);\n",
       "  text-decoration: none;\n",
       "}\n",
       "\n",
       "/* Span, style for the box shown on hovering the info icon */\n",
       ".sk-estimator-doc-link span {\n",
       "  display: none;\n",
       "  z-index: 9999;\n",
       "  position: relative;\n",
       "  font-weight: normal;\n",
       "  right: .2ex;\n",
       "  padding: .5ex;\n",
       "  margin: .5ex;\n",
       "  width: min-content;\n",
       "  min-width: 20ex;\n",
       "  max-width: 50ex;\n",
       "  color: var(--sklearn-color-text);\n",
       "  box-shadow: 2pt 2pt 4pt #999;\n",
       "  /* unfitted */\n",
       "  background: var(--sklearn-color-unfitted-level-0);\n",
       "  border: .5pt solid var(--sklearn-color-unfitted-level-3);\n",
       "}\n",
       "\n",
       ".sk-estimator-doc-link.fitted span {\n",
       "  /* fitted */\n",
       "  background: var(--sklearn-color-fitted-level-0);\n",
       "  border: var(--sklearn-color-fitted-level-3);\n",
       "}\n",
       "\n",
       ".sk-estimator-doc-link:hover span {\n",
       "  display: block;\n",
       "}\n",
       "\n",
       "/* \"?\"-specific style due to the `<a>` HTML tag */\n",
       "\n",
       "#sk-container-id-1 a.estimator_doc_link {\n",
       "  float: right;\n",
       "  font-size: 1rem;\n",
       "  line-height: 1em;\n",
       "  font-family: monospace;\n",
       "  background-color: var(--sklearn-color-background);\n",
       "  border-radius: 1rem;\n",
       "  height: 1rem;\n",
       "  width: 1rem;\n",
       "  text-decoration: none;\n",
       "  /* unfitted */\n",
       "  color: var(--sklearn-color-unfitted-level-1);\n",
       "  border: var(--sklearn-color-unfitted-level-1) 1pt solid;\n",
       "}\n",
       "\n",
       "#sk-container-id-1 a.estimator_doc_link.fitted {\n",
       "  /* fitted */\n",
       "  border: var(--sklearn-color-fitted-level-1) 1pt solid;\n",
       "  color: var(--sklearn-color-fitted-level-1);\n",
       "}\n",
       "\n",
       "/* On hover */\n",
       "#sk-container-id-1 a.estimator_doc_link:hover {\n",
       "  /* unfitted */\n",
       "  background-color: var(--sklearn-color-unfitted-level-3);\n",
       "  color: var(--sklearn-color-background);\n",
       "  text-decoration: none;\n",
       "}\n",
       "\n",
       "#sk-container-id-1 a.estimator_doc_link.fitted:hover {\n",
       "  /* fitted */\n",
       "  background-color: var(--sklearn-color-fitted-level-3);\n",
       "}\n",
       "</style><div id=\"sk-container-id-1\" class=\"sk-top-container\"><div class=\"sk-text-repr-fallback\"><pre>LinearRegression()</pre><b>In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook. <br />On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.</b></div><div class=\"sk-container\" hidden><div class=\"sk-item\"><div class=\"sk-estimator fitted sk-toggleable\"><input class=\"sk-toggleable__control sk-hidden--visually\" id=\"sk-estimator-id-1\" type=\"checkbox\" checked><label for=\"sk-estimator-id-1\" class=\"sk-toggleable__label fitted sk-toggleable__label-arrow fitted\">&nbsp;&nbsp;LinearRegression<a class=\"sk-estimator-doc-link fitted\" rel=\"noreferrer\" target=\"_blank\" href=\"https://scikit-learn.org/1.5/modules/generated/sklearn.linear_model.LinearRegression.html\">?<span>Documentation for LinearRegression</span></a><span class=\"sk-estimator-doc-link fitted\">i<span>Fitted</span></span></label><div class=\"sk-toggleable__content fitted\"><pre>LinearRegression()</pre></div> </div></div></div></div>"
      ],
      "text/plain": [
       "LinearRegression()"
      ]
     },
     "execution_count": 8,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "model = LinearRegression()         # 初始化模型\n",
    "model.fit(x, y)                    # 拟合模型"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "19ddd771-237d-43b2-b761-e44ddc9a1c42",
   "metadata": {},
   "source": [
    "## 获取回归系数和截距"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "id": "c47bf25b-324c-4339-9d5c-26a641b44f4c",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "参数向量 b = [[1.26751592]\n",
      " [0.77707006]]\n"
     ]
    }
   ],
   "source": [
    "b1 = model.coef_                  # 斜率（形状为 (1, 1)）\n",
    "b0 = model.intercept_            # 截距（形状为 (1,)）\n",
    "\n",
    "print(\"参数向量 b =\", np.vstack([b0, b1]))     # 与之前手动求解的 b 对比"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6fef7b21-8e89-4165-b083-e83edc684006",
   "metadata": {},
   "source": [
    "## 计算预测值（用于训练集）"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "id": "d3966530-0933-48cc-aa90-46d5be3e8de1",
   "metadata": {},
   "outputs": [],
   "source": [
    "y_pred = model.predict(x)         # 预测值"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0524c771-6000-4a54-840f-b55489e9907f",
   "metadata": {},
   "source": [
    "## 预测新的 x 区间上的 y 值"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "id": "73bcf2a8-7c99-44ff-a229-d51ec5c46852",
   "metadata": {},
   "outputs": [],
   "source": [
    "x_array = np.linspace(0, 10).reshape(-1,1)\n",
    "y_array_pred = model.predict(x_array)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7c8d5442-bb64-4d4d-9702-33b305b7df6b",
   "metadata": {},
   "source": [
    "## 计算误差向量"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "id": "f7775296-4dd0-4f91-aba7-ccc266cb07e1",
   "metadata": {},
   "outputs": [],
   "source": [
    "error = y - y_pred"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ea6d8104-ff7f-4c91-8466-6c8496bec315",
   "metadata": {},
   "source": [
    "## 计算误差向量的 L2 范数的平方"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "id": "fd245bc6-6980-4065-9121-e01c48d7fb29",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "误差平方和 = [[7.94904459]]\n"
     ]
    }
   ],
   "source": [
    "squared_L2_norm = error.T @ error\n",
    "print(\"误差平方和 =\", squared_L2_norm)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a477340f-fc65-4a52-9905-ebb5ec9984ad",
   "metadata": {},
   "source": [
    "## 可视化"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "id": "0489506f-70a4-4b4c-a5ed-313740bbfea4",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAcwAAAHFCAYAAAB/1MlOAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjkuMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8hTgPZAAAACXBIWXMAAA9hAAAPYQGoP6dpAABJeUlEQVR4nO3deVxVZeLH8c/lqteFJTMQETQxy3KrtMWlJDPTzGwq28ws26asNKfGtilsUavJml9OmlNaaWaL2maaOgXWlIlbkVNWgymYihlecLvI5fz+eAIl0K564TzA9/16+RrPOZfL14vjt+ec5znH4ziOg4iIiBxUhNsBREREqgMVpoiISAhUmCIiIiFQYYqIiIRAhSkiIhICFaaIiEgIVJgiIiIhUGGKiIiEQIUpIiISAhWmiIhICFwtzCVLljBgwAASEhLweDy88847ZY47jkNqaioJCQk0aNCAlJQU1qxZ405YERGp1VwtzJ07d9KpUycmTpxY4fEnn3ySCRMmMHHiRDIyMoiPj+e8886joKCgipOKiEht57Hl5usej4e5c+dy8cUXA2Z0mZCQwMiRIxk9ejQAgUCApk2b8sQTT3DLLbe4mFZERGqbOm4HOJB169axefNm+vTpU7rP5/PRs2dPPv/88wMWZiAQIBAIlG4XFxfz66+/0qRJEzweT6XnFhEReziOQ0FBAQkJCUREHNlJVWsLc/PmzQA0bdq0zP6mTZuyfv36A37duHHjGDNmTKVmExGR6iU7O5vExMQjeg9rC7PE70eFjuMcdKR43333MWrUqNJtv99PixYtyMrK4qijjqqsmIclGAyyZs0a2rVrh9frdTtOKVtzgb3ZbM0F9mazNRfYm83WXGBvtu3bt5OcnExUVNQRv5e1hRkfHw+YkWazZs1K9+fm5pYbde7P5/Ph8/nK7T/qqKNo3Lhx+IMegWAwSGRkJI0bN7bqL5itucDebLbmAnuz2ZoL7M1may6wOxuUH3wdDmvXYbZq1Yr4+HgWLVpUuq+wsJD09HS6devmYjIREamNXB1h7tixgx9//LF0e926daxevZqjjz6aFi1aMHLkSMaOHUubNm1o06YNY8eOpWHDhlx99dUuphYRkdrI1cJcvnw555xzTul2ybXHoUOH8vLLL/PXv/6V3bt3c9ttt5GXl8cZZ5zBwoULw3IuWkRE5FC4WpgpKSkcbBmox+MhNTWV1NTUqgslIiJSAWuvYYqIiNhEhSkiIhICFaaIiEgIVJgiIiIhUGGKiIiEQIUpIiISAhWmiIhICFSYIiIiIVBhioiIhECFKSIiEgIVpoiISAhUmCIiIiFQYYqIiIRAhSkiIhICFaaIiEgIVJgiIiIhUGGKiIiEQIUpIiISAhWmiIhICFSYIiIiIVBhioiIhECFKSIiEgIVpoiISAhUmCIiIiFQYYqIiIRAhSkiIhICFaaIiEgIVJgiIiIhsL4wCwoKGDlyJC1btqRBgwZ069aNjIwMt2OJiEh1sHp12N7K+sK88cYbWbRoEdOnTyczM5M+ffrQu3dvNm7c6HY0ERGx2UsvEdG3b9jerk7Y3qkS7N69m9mzZ/Puu+9y9tlnA5Camso777zDpEmTeOyxx8p9TSAQIBAIlG7n5+cDEAwGCQaDVRM8RCV5lCt0tmazNRfYm83WXGBvNltzgWXZ9uzBc+edREydiieMb+txHMcJ4/uFVUFBAdHR0SxevJhzzz23dH/Xrl3x+XykpaWV+5rU1FTGjBlTbn9aWhqRkZGVGVdERFxWb+NGkkePptF33+FERLB22DBOfPFF/H4/0dHRR/TeVhcmQLdu3ahXrx4zZ86kadOmvP7661x77bW0adOGtWvXlnt9RSPMpKQktm7dSuPGjasy+h8KBoNkZmbSoUMHvF6v23FK2ZoL7M1may6wN5utucDebLbmAkuyzZ9PxLXX4snLwznmGIpnzCCvc2diY2PDUphWn5IFmD59OsOGDaN58+Z4vV5OPfVUrr76alauXFnh630+Hz6fr9x+r9dr3V+wErZmszUX2JvN1lxgbzZbc4G92WzNBS5lKy6GRx4xvxwHTj8dz9tv401KwpuXF7ZvY/2kn9atW5Oens6OHTvIzs5m2bJl7N27l1atWrkdTURE3LZtG/TvD2PGmLK89VZYsgSSksL+rawvzBKNGjWiWbNm5OXl8dFHHzFw4EC3I4mIiJtWrIDOnWHBAmjQAF59FZ5/Hio4yxgO1p+S/eijj3AchxNOOIEff/yRe+65hxNOOIHrr7/e7WgiIuKWF1+E22+HQABat4Y5c6Bjx0r9ltaPMP1+P8OHD6dt27Zce+219OjRg4ULF1K3bl23o4mISFXbvRtuuAFuusmU5UUXwfLllV6WUA1GmJdffjmXX3652zFERMRt69bBpZfCqlUQEQGPPQajR5vfVwHrC1NERIQPP4RrroG8PDjmGJg1C/Zbn18VrD8lKyIitVgwCA8/bGbC5uXBGWfAypVVXpagEaaIiNhq2zYYPBg++shs33YbTJhQabNg/4gKU0RE7LN8ubleuWGDWTIyZYo5JesinZIVERF7OI4px+7dTVkedxwsXep6WYIKU0REbLF7NwwbBrfcAoWFMHBglS0ZCYUKU0RE3JeVBd26wcsvm2Ui48ebmxHExLidrJSuYYqIiLs++ACGDIHt2yE21iwZ6dXL7VTlaIQpIiLuCAbhb3+DAQNMWZ55plkyYmFZgkaYIiLihl9+gauvhkWLzPbtt8PTT0O9eu7mOggVpoiIVK1ly+CyyyA7Gxo2hH/9y5Sn5XRKVkREqobjwOTJcNZZpizbtIEvv6wWZQkqTBERqQq7dsH115sHPBcWwp/+BBkZ0L6928lCplOyIiJSuf73P3PXnq++2rdk5O67weNxO9khUWGKiEjlef99s2TE74e4OLNk5Jxz3E51WHRKVkREwi8YhAceMA949vuha1ezZKSaliWoMEVE5HD5/ZCTU37/1q2mGMeONdt33glpadC8eZXGCzcVpoiIHDq/H/r2hZ49zYzXEl9+CSefDJ9+aq5Xvvgi/OMfVq+vDJUKU0REDl1BAeTmmnvApqRAdjbHvP02ESkp8PPP5jUJCXD++W6mDCtN+hERkUOXmGhOs6akQFYWni6daOnPh6Lfjh97LCxZYl5XQ2iEKSIihycpyTxdpGFdIgbk4wzxUNywLrRqZcoyKcnthGGlwhQRkcPz7rtw4YVQuBcH2Na/P07dujBjRo0rS1BhiojIoSoqgvvug4svhvx88NXDqVOHwmbNzPEhQ8pOBKohVJgiIhK63FwzkWf8eLMdHQ2BQmjc2Gy3bFlmIlBNosIUEZHQLF0KnTvDxx+bp4zExZkRZqtW+26g/u67kJy8rzQrWqdZTakwRUTk4BwH/vlPOPtsU4AnnGBKMznZ/Fq8yIw0AZr/Nns2OdkUalSUq9HDSctKREQw6/ALCipeBZGTY/7dj4mp+lyu27kT/vxnM5EHzHMsX3oJoqPxv7GAXVsKaJYYBz/t9zVJSWyalU7DplHE1KAPzeoRZlFREQ8++CCtWrWiQYMGJCcn88gjj1BcXOx2NBGpQQ500xow2z17muN+vzv5XPPDD3DmmaYsvV54+ml4801Tln7oe0UMPa5MJPt3Z12zs6HHlYn0vSKmRn1mVo8wn3jiCSZPnswrr7xCu3btWL58Oddffz0xMTGMGDHC7XgiUkP8/qY1H39s9ufkQK9eZn/J62rQgOng3nkHhg411yjj4+GNN8wp2d/s/5md1xuWvmn252yEXufWzM/M6hHmF198wcCBA+nfvz/HHnssl112GX369GH58uVuRxORGiRxv8tuWVkOAy8qxOPspX9/h6wssz8trUbdtObAiorg3nvNA57z86FHD/OUkf3KEn73ma2DmTPN/oEDqbGfmdUjzB49ejB58mS+//57jj/+eL766is+++wznn322QN+TSAQIBAIlG7n5+cDEAwGCQaDlR35kJTkUa7Q2ZrN1lxgbzbbciUkmJHlwIsKGdxjLPGFW8j7tT3t2jVg3jxz3O2olf6ZbdlCxODBeNLSACgeMQJn/HioW7fCP/y+zyzIzp0OAFu3BmnXLmjdZxYOHsdxnLC9W5g5jsP999/PE088gdfrJRgM8vjjj3Pfffcd8GtSU1MZM2ZMuf1paWlERkZWZlwRqQE8zl7iC6cCsLneMBxPXZcTVY1GX39N8ujR1Nu6lWCDBqx/6CHyzjsvpK+1+TPbsWMHKSkp+P1+oktm8h4mqwtz1qxZ3HPPPTz11FO0a9eO1atXM3LkSCZMmMDQoUMr/JqKRphJSUls3bqVxiULay0RDAbJzMykQ4cOeL1et+OUsjUX2JvN1lxgbzYbc+Xk7BthDh68hdMum0Djo80I04ZTi5XymTkOnn/+E8/dd+MpKsJp25bit96CE08M6ctzcqB/f4e8X/fw/nvf0PeCU4iLq2PNZ5aXl0dsbGxYCtPqU7L33HMP9957L1deeSUAHTp0YP369YwbN+6Ahenz+fD5fOX2e71ea/5P+Xu2ZrM1F9ibzdZcYG82W3JlZ5sJPtkbvDQ63wNAbKyXr7720quXuR5ny+1Rw/aZ7dwJN90Er79utgcNwvPSS3hDXDtZ8pllZUG7dg1wPHWJi6vDmjX2fGbh/Ltl9aSfXbt2ERFRNqLX69WyEhEJq5yc0qdUkVw7bloD338PZ5xhytLrhQkTzEzYEMuyzGeWDPPmmf3z5tXcz8zqEeaAAQN4/PHHadGiBe3atWPVqlVMmDCBYcOGuR1NRGqQqChzUxqARYshugh27YLE5vse+VijblozZw5cd51Z8xEfb9ZWnnXWIb3F/p9ZWpqZ4PPLL2Ufk1mjPjMsL8znnnuOv/3tb9x2223k5uaSkJDALbfcwkMPPeR2NBGpQWJiYMGC3+70kwDFP+07lpQE6ek15E4/RUVw//3w1FNm+6yzzKiy5Ckjh6DMZ5ZYdjZsjfrM9mN1YUZFRfHss88edBmJiEg4xMT89o97BVd8bJi8csS2bIErrzTDP4C//AXGjTNLRg5T6WdWgRrxmf2O1YUpIiJh8PnnMGgQ/PwzREbCtGnmnrBySKye9CMiIkfAceD//s/cDPfnn81SkYwMleVhUmGKiNREO3aY6b4jRphrl1dcAcuWQdu2biertnRKVkSkplm7Fi65BP77X6hTB/7+d7jzTvB43E5WrakwRURqktmz4frrzfTVZs3grbege3e3U9UIOiUrIlITFBXBPfeY65MFBea65cqVKsswUmGKiFR3mzdD797m1CvA3XfD4sXmpgQSNjolKyJSnf3nP2bJyKZN5k4B06bBpZe6napG0ghTRMRmfn/FN2R1HBgzxtyDbtMmOOkks2REZVlpNMIUEbGV3w99+0Ju7r4btoJZMjJkCLz/vtm+7DIzstQzfyuVRpgiIrYqKDBlud+jP3w//UREly77yrJJE/OkEZVlpdMIU0TEVvs/+iMrC3p258S8X/Hk7zbHExJg6VL3HzpZS2iEKSJis6QkWLQImkTjPWcj3kt249TzQIsWKssqpsIUEbHZpk3m2ZX+fAB2dOpIcf365sHPKssqpcIUEbHVp5/Cqaea//VAsddL/ulnmFvcDRkC2dluJ6xVVJgiIrZxHHjmGTjnHHNTgrp1wQGOPtocb9ly30QglWaVUWGKiNikoMA8WWTUKAgGzezXvXuhVSvz9BGAd9+F5OQys2el8qkwRURs8e23cPrp5obpdevCk09Cu3amHBcvguho87rmv82eTU6GuDhzhx+pdFpWIiJigzffhGHDYOdOaN7clGbXrnDzzWbUmRAHP+33+qQkSE83ZRkT41bqWkWFKSLipr17YfRoc80SzHXLWbPMyBFMGcbEQHFh+a9NTKy6nKJTsiIirtm0CXr12leWo0fDwoX7ylKsohGmiIgbliyByy+HLVvMtclXXoGLL3Y7lRyERpgiIlXJceDpp83IcssWaN8eli9XWVYDGmGKiFSV/HwzsWf2bLN9zTUweTI0auRuLgmJClNEpCqsWWOeVbl2rVky8swzcNtt5q49Ui2oMEVEKtusWXDjjWbJSGKiWTJy5plup5JDpGuYIiKVpbAQRoyAq64yZdmrF6xceUhl6fcf+EY+OTnmuFQN6wvz2GOPxePxlPs1fPhwt6OJiBzYzz+bNZX/939m+777zJKR2NiQ38Lvh759oWdPyP5daWZnm/19+6o0q4r1p2QzMjIIBoOl29988w3nnXcegwYNcjGViMhBpKWZ+8Hm5polI6++CgMHHvLbFBSYt8jKgvN6w9I3zf6cjdDrXLO/5HW62U/ls36EGRsbS3x8fOmvDz74gNatW9OzZ0+3o4mIlOU48NRT0Lu3abqOHWHFisMqSzCXO0tuGZu1DmbONPsHDjRlmZxsjuuGP1XD+hHm/goLC5kxYwajRo3Cc4CZZYFAgEAgULqdn28euhoMBsuMVG1Qkke5QmdrNltzgb3ZbM1FcdAUH79liwgxX34+ETfcgGfuXPM211yD8/zz0LCheerIYUpIgI8/hoEXBdm50+TaujVIu3ZB5s0zx234CG39eYYzj8dxfvubUQ28+eabXH311WzYsIGEhIQKX5OamsqYMWPK7U9LSyMyMrKyI4pINedx9hJfOBWAzfWG4Xjq/uHX1P/f/2h9zz3U37CB4jp1yL77bn659NKwLhk5nFwCO3bsICUlBb/fT3TJ014OU7UqzPPPP5969erx/vvvH/A1FY0wk5KS2Lp1K40bN66KmCELBoNkZmbSoUMHvF6v23FK2ZoL7M1may6wN5utuSguhA1j2bJlC8ecOgFv3QYHfbln1iw8N9+MZ9cunMREit94A844I6yRcnKgf3+HvF/38P5739D3glOIi6vDvHn2nI619eeZl5dHbGxsWAqz2pySXb9+PYsXL2bOnDkHfZ3P58Pn85Xb7/V6rfoh7s/WbLbmAnuz2ZoL7M1mXS6Pl+LfRoYHzVZYCHffDc89Z7Z798bz+ut4jzkmrHGys81qlKwsaNeuAY6nLnFxdVizxkuvXuYaZlJSWL/lEbHt5xnOLNZP+ikxbdo04uLi6N+/v9tRRKS2y8mBlJR9ZfnAA7BgAYS5LEu+TckEn3nzzP55836bCJRljh9onaaEV7UYYRYXFzNt2jSGDh1KnTrVIrKI1FSffGKWjGzdatZyTJ8OAwZUyreKitr3pK+0NDPB55df9s2eTUkxx6OiKuXby+9Ui/ZZvHgxGzZsYNiwYW5HEZHaynHgySfh/vuhuBg6dTI3UW/dutK+ZUyMGbgWFJiS3H/CZ1ISpKebstQazKpRLQqzT58+VKO5SSJS0/j9cN118M47ZnvoUChZMlLJYmIOXIi2TPipLapFYYqIVDq/3wzlEuLK7s/MNHcKWLcO6tUzt7q7+WY9ZaQWUmGKiJTctDU3Fz5ZWLrbM2sW3DIcdu82ZTl/vpmyKrVStZklKyJSafa/aWvv8yAvj5j//IeIYcNMWQI0awbHH+9uTnGVRpgiIvtPO12XhWfyZBoVFe073qqVmWGji4a1mkaYIiJgpp0+/jhEePAUFVFcrx5BXz2z4DE93a67A4grVJgiIo4D48fD4MFQ7OB4YOslf4I6dcw6S5WloMIUkdrO74c//ck84Lm4GOo3onh2AzY2GQlFwJAh5v50UuupMEWk9vr6a+jSBd5918yCPeYY2LETkpLN00CObbXv/nMqzVpPhSkitdOMGXDmmfDjj2YyT2ysue+cbtoqB6DCFJHaJRCA4cPNqdbdu+H882HJEnOdMjnZzJYtmQ1bMns2OVk3bRUtKxGRWiQ7GwYNgi+/NNsPPWR+eb26aav8IRWmiNQO//43XHmlOe3auLE5JXvBBfuO66at8gd0SlZEarbiYhg7Fvr0MWV5yimwYkXZshQJgUaYIlJzbd9unizy3ntme9gwmDgRGjRwNZZUTypMEamZvvoKLr0U/vc/8PlMUd54o9uppBpTYYpIzfPqq/DnP5tZsMceC2+/DZ07u51KqjldwxSRmiMQgFtvNadhd+82j+xasUJlKWGhwhSRmmHDBjj7bJg82TzcOTXV3Hjg6KPdTiY1hE7Jikj1t2gRXHUVbNtmlozMnGlGlyJhpBGmiFRfxcXmkVznn2/KsnNnWLlSZSmVQiNMEame8vLg2mvhgw/M9o03wnPPQf367uaSGkuFKSLVz+rVZslIVpZZMvL882aNpUglUmGKSPXyyitmyciePWbJyOzZcOqpbqeSWkDXMEWkeggETFFed50pywsuMEtGVJZSRVSYImK/9euhRw944QWzZOSRR+D997VkRKqUTsmKiN0WLjRLRn791RTka69pFqy4QiNMEbFTcTE8+qgpx19/hS5dtGREXKXCFKmB/H7Iyan4WE6OOW61vDy46CLzcGfHgZtvhk8/hZYt3U4mtZj1hblx40auueYamjRpQsOGDTn55JNZsWKF27FErOX3m0FYz56QnV32WHa22d+3r8WluWqVuQHBvHlmTeXUqebapdZXisusvoaZl5dH9+7dOeecc5g/fz5xcXH873//46ijjnI7moi1CgogN9csUUxJgY8/NvtzcqBXL7O/5HUxMVUczu833zgxsfyxnBx49134y1/MjNhWrcySkVNOqeKQIhWzujCfeOIJkpKSmDZtWum+Y4891r1AItVAYiKkpZmyzMpyGHhRIVNf2kv//g5ZWZCcbI5X1FmVqmTom5trAiQk7Dv2ww9mVFlQYLb794fp0819YUUsYXVhvvfee5x//vkMGjSI9PR0mjdvzm233cZNN910wK8JBAIEAoHS7fz8fACCwSDBYLDSMx+KkjzKFTpbs9mWKyHBjCwHXlTI4B5jiS/cQt6v7WnXrgHz5pnjVR61ZHS5ZQv060fw/fcBCH7+ORF9+uDZswcHcEaNwhk/HiIiXAhp2PbzLGFrLrA3WzjzeBzHccL2bmFW/7drFqNGjWLQoEEsW7aMkSNH8sILL3DttddW+DWpqamMGTOm3P60tDQiIyMrNa+IbTzOXuILpwKwud4wHE9dlxP9xnHwUETU0i9o9WAqdfz5FMXEkPX44xSceabb6aQG2bFjBykpKfj9fqKjo4/ovawuzHr16tGlSxc+//zz0n133nknGRkZfPHFFxV+TUUjzKSkJLZu3Upjy07vBINBMjMz6dChA16v1+04pWzNBfZmszFXTs6+EebgwVs47bIJND7ajDCr/HTs74MN6IfnzB/w7C3CMwOcug0oXrQILClLG3+eYG8usDdbXl4esbGxYSlMq0/JNmvWjJNOOqnMvhNPPJHZs2cf8Gt8Ph8+n6/cfq/Xa9UPcX+2ZrM1F9ibzZZc2dlmgk/2Bi+NzvcAEBvr5auvvfTqZS4hJiW5FC4qCqKjYG8RAMV1vER8+CHe7t1dCnRgtvw8f8/WXGBftnBmsXpZSffu3Vm7dm2Zfd9//z0ttRZL5IByckom/EByK7j6arP/3XfNhJ+S2bMHWqdZqVauhJNPhi+X4QB5KT1xfD644Ybya2BELGN1Yd51110sXbqUsWPH8uOPPzJz5kymTJnC8OHD3Y4mYq2oKIiLM+W4aDGUnIVKbG5GlsnJ5nhUVBUHe+kl6NattBidxo3Z3eZ4czOCkhZXaYrFrC7M0047jblz5/L666/Tvn17Hn30UZ599lkGDx7sdjQRa8XEwIIFkJ4OSb+7VpmUZPYvWFCFazD37DEPd77xRrO+EqBFC/PwZ7Bk6Cvyx6y+hglw4YUXcuGFF7odQ6RaiYn5rRCLyx+r0gk/69bBZZeZU7EREdC8OdSpA58sgqJXYNcuaL7fwlFXhr4iobG+MEWkmpo/HwYPNveFPeYYeP11OO00sxYzIQ5+2u+1JUPfqCgXbj8kEhoVpoiEVzBonlf56KPmxumnnw5vvWVOw4IpxOLC8l/n6loXkT+mwhSR8Nm2Da65xlwkBbj1VnjmGahgqZdIdaPCFJHwWL7cXK9cvx4aNDBPGBkyxO1UImFj9SxZEakGHAf+9S/o3t2UZevWsHSpylJqHBWmiBy+3bvNTQduvhkKC81Dn5cvh44d3U4mEnYqTBE5PFlZZlQ5bZpZMjJ2LMydC3perdRQuoYpIodu3jwzuWf7doiNNUtGzj3X7VQilUojTBEJXTAIDz0EF15oyvKMM8xNCVSWUgtohCkiofnlFzOq/Ogjsz18OEyYAPXquZtLpIqoMEXkj2VkmCUjGzaYJSP/+pe5i49ILaJTsiJyYI4DU6ZAjx6mLI87Dr78UmUptZIKU0Qqtns3DBsGt9xiloxcfLFZMtKhg9vJRFyhwhSR8rKyzLMrX37ZLBkZPx7mzNGN0aVW0zVMESnrgw/MXXpKlozMmgW9ermdSsR1GmGKiBEMwt/+BgMGmLI880yzZERlKQJohCkiYJaMXH01LFpktm+/HZ5+WktGRPajwhSp7ZYtM0tGsrOhYUMzK1azYEXK0SlZkdrKcWDyZDjrLFOWxx+vJSMiB6HCFKmNdu2C664zD3guLIQ//cncnKB9e7eTiVhLhSlSE/n9kJNT8bFPP4XTT4dXXzVLRp58EmbPhujoqs0oUs3oGqZITeP3Q9++kJsLnywse2zqVLjpJiguNktG3nwTUlJciSlS3WiEKVLTFBSYsszKgt7nQX4+OA6eUaPMw56Li8HnM4/oUlmKhEwjTJGaJjER0tJMGa7LghkzaFLfR8Tkn83x6GizvrJ1azdTilQ7GmGK1ERJSaY0mzXDs20bvo0/4wDExcE336gsRQ6DClOkJnIceP992JqLByiKiaG4QX2YO9eUqYgcMhWmSE2zaxdce615wPOeIM7rEXzzp7ehOMLcIzY727VoB5u8m5NjjovYSoUpUpP8+CN07QozZpTuKm7dlmBkYzi2lZkIlJLiSmmWTN7t2ROyf1ea2dlmf9++Kk2xl9WFmZqaisfjKfMrPj7e7Vgidnr3XejcGb7+Grxesy85GT780Px+3jyzXVKaBxrqVZL9J++e19tM3gXI2WjiZGWZ4wUFVRpLJGSHXJjXXXcdS5YsqYwsFWrXrh2bNm0q/ZWZmVll31ukWigqgvvuMw94zs83Txnp1MmUY1qamTUL+2bPJiebyT9RUVUac/9vn7UOZs40+wcONGX5+7gitjnkZSUFBQX06dOHpKQkrr/+eoYOHUrz5s0rIxsAderUOaRRZSAQIBAIlG7n//afscFgkGAwGPZ8R6Ikj3KFztZsruXKzSVi8GA8n3wCQPGdd+I88QTs3m2GagkJZbMlJMAnn5iyjIw0j/SqQgkJ8PHHMPCiIDt3OgBs3RqkXbsg8+aZ4zb8aPX37NDZmi2ceTyO4ziH+kXbtm1jxowZvPzyy3zzzTf07t2bG264gYEDB1K3bt2whUtNTeWpp54iJiYGn8/HGWecwdixY0lOTj7o14wZM6bc/rS0NCIjI8OWTcRtjTIzSR49mnq5uQQbNGD93/5GXp8+bscKicfZS3zhVAA21xuG4wnfvxsi+9uxYwcpKSn4/X6ij/D2j4dVmPtbtWoVU6dO5cUXXyQyMpJrrrmG2267jTZt2hxRMID58+eza9cujj/+eLZs2cJjjz3Gd999x5o1a2jSpEmFX1PRCDMpKYmtW7fSuHHjI84UTsFgkMzMTDp06IC35JqTBWzNBfZmq9JcjoPn+efx3H03nr17cdq2pfjNN+Gkk9zPFoKcHOjf3yHv1z28/9439L3gFOLi6jBvnj2nY237zErYmgvszZaXl0dsbGxYCvOI7vSzadMmFi5cyMKFC/F6vVxwwQWsWbOGk046iSeffJK77rrriML169ev9PcdOnSga9eutG7dmldeeYVRo0ZV+DU+nw+fz1duv9frteqHuD9bs9maC+zNVum5du6EW26B114z25ddhmfqVLwhXI+04TPLzoZevcw1y3btGuB46hIXV4c1a7z06mWuYdq0TNSGz6wituYC+7KFM8shT/rZu3cvs2fP5sILL6Rly5a89dZb3HXXXWzatIlXXnmFhQsXMn36dB555JGwhSzRqFEjOnTowA8//BD29xax3vffmwk9r71mZsE+/bS5eXoVT945XDk5+2bDJiebSbvg+uRdkZAd8gizWbNmFBcXc9VVV7Fs2TJOPvnkcq85//zzOeqoo8IQr6xAIMC3337LWWedFfb3FrHa3Lnm+ZX5+RAfD2+8AWef7XaqQxIVZSbnghlJJiTAL7+UvfWtC5N3RUJ2yIX5zDPPMGjQIOrXr3/A1zRu3Jh169YdUTCAu+++mwEDBtCiRQtyc3N57LHHyM/PZ+jQoUf83iLVQlERPPCAeWYlQI8eZlTZrJm7uQ5DTAwsWGAm7yYmlp0Nm5QE6emmLGNi3MsocjCHXJhDhgypjBwVysnJ4aqrruKXX34hNjaWM888k6VLl9KyZcsqyyDimi1b4KqrzDIQgLvugieegDDORK9qMTEHLkRbJvyIHIjVj/eaNWuW2xFE3PHFF3DZZfDzz9CokXnw8+WXu51KpFaz+tZ4IrWO48Bzz5nrkz//DG3bQkaGylLEAipMEVvs3AmDB8Odd5prl4MGwbJlcOKJbicTESw/JStSa3z/PVxyCaxZA3XqmEk+I0eCx+N2MhH5jQpTxG1z5pglIwUFZsnIW2+Z2bAiYhWdkhVxS1ER/PWvcOmlpizPPhtWrVJZilhKhSnihs2boXdveOops/2Xv8DixWaEKSJW0ilZkar2n/+YCT2bNplHbE2bZpaQiIjVNMIUqSqOA//4h7kH3KZNZvZrRobKUqSaUGGKVIUdO+Dqq83M16IiuOIKs2SkbVu3k4lIiHRKVqSyffedmdjz3/+aJSN//7tZa6klIyLVigpTpDK9/TZcf70ZYTZrZpaMdO/udioROQw6JStSGYqK4O67zeSeHTugZ09YuVJlKVKNqTBFwm3zZjj3XPOAZ4B77tGSEZEaQKdkRcLps8/MI7k2bTIPd3z5ZXPLOxGp9jTCFAkHxyFu5kwievc2ZXnSSWbJiMpSpMbQCFPkSO3YgeeGG0h6802zfdVVMGWKuSmBiNQYKkyRI/Hdd3DJJUR8+y2O14vz9NNEaMmISI2kU7Iih+vtt+G00+Dbb3ESElg7ZQrO7berLEVqKBWmyKHau9fcLL1kyUhKCsUZGezs1MntZCJSiVSYIodi0yazZGTCBLP917/CokXQtKm7uUSk0ukapkioPv0ULr/crLOMioJXXoE//ckcCwbdzSYilU4jTJE/4jhmRHnOOaYs27eH5cv3laWI1AoaYYocTEEB3HCDuQcsmCeOTJkCjRq5m0tEqpwKU+RAvv3W3Hjgu++gbl145hm47TbNghWppVSYIhV5800YNgx27oTmzc0Is2tXt1OJiIt0DVNkf3v3wl13mQc879wJvXqZp4yoLEVqPRWmWM/vh5ycio/l5JjjYfHzz2Ziz7PPmu1774WPPoK4uDB9AxGpzqpVYY4bNw6Px8PIkSPdjiJVxO+Hvn3N4ySzs8sey842+/v2DUNppqfDqafCf/4D0dHwzjswbhzU0VULETGqTWFmZGQwZcoUOnbs6HYUqUIFBZCbC1lZkJKyb6SZk2O2s7LM8YKCw/wGjmOeW3nuubBlC3ToYJaMDBwYpj+BiNQU1aIwd+zYweDBg/nXv/5F48aN3Y4jVSgxEdLSIDkZsrIcBl5UiMfZS//+DllZZn9amnndIcvPN7e3u/tuc+OBa66BpUuhTZsw/ylEpCaoFuebhg8fTv/+/enduzePPfbYQV8bCAQIBAKl2/n5+QAEg0GClt2NpSSPch1cQgJ8/DEMvKiQwT3GEl+4hbxf29OuXQPmzTPHDznqmjVEXH45nrVrcerWxZkwAefPfzZLRg7jz23bZ7Y/W7PZmgvszWZrLrA3WzjzeBzHccL2bpVg1qxZPP7442RkZFC/fn1SUlI4+eSTebZkYsbvpKamMmbMmHL709LSiNTzCas1j7OX+MKpAGyuNwzHU/ew3qfxRx/R8rHH8O7eTWHTpmSNH8/ODh3CGVVELLFjxw5SUlLw+/1ER0cf0XtZPcLMzs5mxIgRLFy4kPr164f0Nffddx+jRo0q3c7PzycpKYl27dpZdzo3GAySmZlJhw4d8Hq9bscpZWOunJySEWZTBg/ewoCL2tP4aDPCDPl0bGEhntGjiXjuOQCcXr3wvvYabWJjjzifjZ9ZCVuz2ZoL7M1may6wN1teXl7Y3svqwlyxYgW5ubl07ty5dF8wGGTJkiVMnDiRQCBQ7gfj8/nw+Xzl3svr9Vr1Q9yfrdlsyZWdbZZDZm/w0uh8c5ed2FgvX33tpVcvcw0zKekP3mTjRnPj9M8/N9v334/nkUfC/uez5TOriK3ZbM0F9mazNRfYly2cWawuzHPPPZfMzMwy+66//nratm3L6NGjrfqhSOXYfzbsCW3MrVx37YJ334Ve5+6bPZuefpCRZlqauRFBbi7ExMCrr8JFF1XdH0JEagSrCzMqKor27duX2deoUSOaNGlSbr/UTFFR++4bsGgxRBeZwkxsbnowJcUcj4qq4IsdB/7+d7jvPjORp2NHmD0bjjuuCv8EIlJTWF2YIjExsGCBWWeZmADFP+07lpRkRpZRUeZ1ZeTnw/XXw5w5ZnvIEJg8GRo2rKroIlLDVLvCTEtLczuCVLGYmN8Ksbj8sQpPw37zDVx6KXz/vXnKyP/9H9xyi54yIiJHpNoVpshBzZwJN93023nbRHj7bTjjDLdTiUgNUC3u9CPyhwoL4Y47YPBgU5a9e5unjKgsRSRMVJhS/ZVMpZ040Ww/8IC58BmG9ZUiIiV0Slbs5/ebWT8JFTxm6803Yfhw+OUXc6Fz+nQYMKDqM4pIjafCFLuVPN8rNxc+Wbhvv+OY5SLjx5vt9u3NI7lat3YlpojUfDolK3bb//levc+D/Hw8ewuJGDhwX1lGRprlIypLEalEGmGK3Uqe75WSAuuy4NVXiS0O4llsnkLDMcfAihXQooWbKUWkFtAIU+yXlGRKM/YYPHl51PHn43gwz/VauVJlKSJVQoUp9isshCeegK2/4AECzZtT3KABvPVWCHddFxEJDxWm2C0nB84+G/75TyiC4jfqsGbAGxD0mNvdZWe7nVBEagkVptjr3/+GU06BL7+ECPNX1Uk+AadOfTi21b5Hlag0RaQKqDDFPsXFMG4c9Olj1lfWq2f2JSfDvHnmNfPmme2S0szJcTWyiNR8miUrdtm+HYYOhffeM9tXXw0//miKMy3NTPT55Zeys2cP+HwvEZHwUWGKPb7+Gi65BP73PzOqfO45cyP1/Pzfnu+VaJ5rWeKgz/cSEQkvFabYYfp08wiu3bvNMpG334bTTjPHSp/vVYEKn+8lIhJ+uoYp7goE4Lbb4NprTVn26WNuRFBSliIillBhinuys82SkUmTzPZDD8GHH5q794iIWEanZMUdixfDVVeZCTxHHQWvvQYXXOB2KhGRA9IIU6pWcTGMHQvnn2/K8pRTzO3tVJYiYjmNMKXqbN9urlW+/77Zvv56cwefBg1cjSUiEgoVplSNr76CSy81S0Z8Ppg4EW680e1UIiIhU2FK5Xv1VbNkZM8eaNkSZs+Gzp3dTiUickh0DVMqTyAAt95q7tyzZw/07WuWjKgsRaQaUmFK5diwwSwZmTwZPB54+GH44ANo0sTtZCIih0WnZCX8Fi0yS0a2bYPGjc2SkX793E4lInJENMKU8CkuhscfN0tGtm2DU081p2BVliJSA6gwq5jff+AnUeXkmOPVUl4eDBwIDz4IjgM33AD/+Q+0auV2MhGRsLC6MCdNmkTHjh2Jjo4mOjqarl27Mn/+fLdjHTa/38x76dmz/DOPs7PN/r59q2Fprl4NXbqYa5Q+H7z4ovlVv77byUREwsbqwkxMTGT8+PEsX76c5cuX06tXLwYOHMiaNWvcjnZYCgogN7f8M49zcsx2VpY5XlDgZspD9PLL0LWrCX/ssfD552Z0KSJSw1hdmAMGDOCCCy7g+OOP5/jjj+fxxx8nMjKSpUuXuh3tsJQ88zg5GbKyHAZeVIjH2Uv//g5ZWWZ/Wlo1eWJVIGDWVl5/vVkycsEF5nrlqae6nUxEpFJUm1mywWCQt956i507d9K1a9cDvi4QCBAIBEq38/PzS78+uP/Dh12SkAAffwwDLypkcI+xxBduIe/X9rRr14B588xxt2OWfE4H/LzWryfiiivwLF+O4/HgPPQQzgMPQEREpYf/w2wusTUX2JvN1lxgbzZbc4G92cKZx+M4jhO2d6sEmZmZdO3alT179hAZGcnMmTO54CA36k5NTWXMmDHl9qelpREZGVmZUQ+Jx9lLfOFUADbXG4bjqetyotBEf/EFrR58kDp+P0UxMax79FHyu3VzO5aISIV27NhBSkoKfr+f6OjoI3ov6wuzsLCQDRs2sH37dmbPns2LL75Ieno6J510UoWvr2iEmZSUxNatW2ncuHFVxT6onJx9I8zBg7dw2mUTaHy0GWHacDo2GAySmZlJhw4d8Hq9ZmdxMZ6xY/GMGYPHcXA6d6b4jTfMdUu3s1nA1lxgbzZbc4G92WzNBfZmy8vLIzY2NiyFaf0p2Xr16nHccccB0KVLFzIyMvjHP/7BCy+8UOHrfT4fPp+v3H6v12vFDzE7G3r1guwNXhqd7wEgNtbLV1976dXLXMNMSnIhmN9vZhvt19iln1lmJtx9NyxcaA7cfDOef/wDr4uzYG35ef6erbnA3my25gJ7s9maC+zLFs4sVk/6qYjjOGVGkNXJ/rNhk1vB1Veb/e++WzIRqOzs2SpzsPUu8+ebiTwLF5plIlOnwgsvaMmIiNQ6Vo8w77//fvr160dSUhIFBQXMmjWLtLQ0FixY4Ha0wxIVBXFx5veLFkN0EezaBYnNzcgyJcUcj4qq4mC/X+/y8ccAeCZMgNGjzY0I6tSBuXNNsYqI1EJWF+aWLVsYMmQImzZtIiYmho4dO7JgwQLOO+88t6MdlpgYWLDgtzOfCVD8075jSUmQnm7KMiamioOVrHcpGf5e2JcWJ5xAxOx3zfGGDWHpUujQoYqDiYjYw+rCfOmll9yOEHYxMb8VYnH5Y65O+ElKMqV5djciTvuOWOc7nDrgiWoMq1aZ51iKiNRiVhemVLE1ayDfj8eBYp8Px+fg/eADlaWICNVw0o9UguJiGDPG3K0nvwDHA1svucRctxwypPxEIBGRWkiFWdv9+iv07w+pqWZyD+A0OYZgZKQZWZZMBFJpikgtp8KszUru/bpgAXjMmlBatYLBg83vXV/vIiJiDxVmbfXSS9C9O6xfb+7W0769KcfFi6DkbhjN97tbvCvrXURE7KFJP7XN7t1w++3mBgQAF14Ir75qbpxeUAAJcfDTfq93db2LiIg9VJi1ybp1cNllsHKlOQX76KNw332mLMEUYnFh+a+z4Qa3IiIuU2HWFvPnm2uTeXnQpAm8/jpU0xtAiIi4Qdcwa7riYjMDtn9/U5annWZGmCpLEZFDohFmTbZtG1xzjZkFC3DrrfDMM1DB01xEROTgVJg11YoVcOmlZhZsgwbmCSNDhridSkSk2tIp2ZroxRf3LRlp3Rq++EJlKSJyhFSYNcnu3XDDDXDTTRAIwEUXwfLl0KmT28lERKo9FWZNkZVlRpVTp5plImPHmudXHnWU28lERGoEXcOsCT780CwZ2b4djjnGLBnp3dvtVCIiNYpGmNVZMAgPPWSWjGzfDmecYZaMqCxFRMJOI8zqats2M6r86COzfdttMGGCloyIiFQSFWZ1lJFhbnG3YYNZMjJlillvKSIilUanZKsTxzHl2KOHKcvjjoOlS1WWIiJVQIVZXezeDcOGwS23QGEhDBxoRpodO7qdTESkVlBhVgdZWdCtG7z8slkyMn48zJmjJSMiIlVI1zBt98EH5i4927dDbCzMmgW9ermdSkSk1tEI01bBIPztbzBggCnLM880S0ZUliIirtAI00a//GKWjCxcaLZvvx2efhrq1XM3l4hILabCtM3+S0YaNjSzYgcPdjuViEitp1OyVc3vh5yc8vsdx0zmKVky0qYNfPmlylJExBIaYVYlvx/69oXcXPhk4b79u3bBjdfC22+b7QsvhBkzICbGnZwiIlKORphVqaDAlGVWFvQ+D/Lz8RbkE9Gjx76yPPpoeP55laWIiGWsLsxx48Zx2mmnERUVRVxcHBdffDFr1651O9bhS0yEtDRIToZ16+DlacTOmYPnu+/M8WbNYPVqSEpyM6WIiFTA6sJMT09n+PDhLF26lEWLFlFUVESfPn3YuXOn29EOX1IS/PvfcFQMEf58Igr34kR4oEULc81SZSkiYiWrr2EuWLCgzPa0adOIi4tjxYoVnH322S6lOkJbt8JNN8F2PwA727Wjfv0svK+/rrIUEbGY1YX5e36/KZmjjz76gK8JBAIEAoHS7fz8fACCwSDBYLByA/6RZcuIuOIKPNnZOB4IvhvJ2uEv0v7JAXDzzTBvnjlt66bioJmxi/nMiHD5M/udkp+h6z/L37E1F9ibzdZcYG82W3OBvdnCmcfjOL/962g5x3EYOHAgeXl5fPrppwd8XWpqKmPGjCm3Py0tjcjIyMqMeGCOwzGzZ5P0978TUVTEnhYt+N+TT7LnuOPcyXMQHmcv8YVTAdhcbxiOp67LiUREDt+OHTtISUnB7/cTHR19RO9VbQpz+PDhzJs3j88++4zEg4zCKhphJiUlsXXrVho3blwVUcvatQvPbbcRMWMGAE5UFMXBILRqRfD998n89Vc6HH003gED4Kef4Nhj4cMPoXnzqs8KUFwIG8ayZcsWjjl1At66DdzJcQDBYJDMzEw6dOiA1+t1O04pW3OBvdlszQX2ZrM1F9ibLS8vj9jY2LAUZrU4JXvHHXfw3nvvsWTJkoOWJYDP58Pn85Xb7/V6q/6H+OOPcOml8PXX4PVCaiqeDz7Au3UrzJ8PCQnw6694W7TAO38+pKRAVJRZUuLWXziPl2KPB3DpMwuRrdlszQX2ZrM1F9ibzdZcYF+2cGaxujAdx+GOO+5g7ty5pKWl0apVK7cjhe699+Daa83NCpo2hTfegJ494Y47zHrMxERzg/USSUmQnr6vMEVExCpWF+bw4cOZOXMm7777LlFRUWzevBmAmJgYGjSw61RhqaIieOghGDfObHfvDm++aUaTYMrwQIXo9oQfERE5IKvXYU6aNAm/309KSgrNmjUr/fXGG2+4Ha1iublw/vn7ynLECPjkk31lKSIi1ZbVI8xqMh/JWLoUBg0yN1Zv1AhefBGuvNLtVCIiEiZWjzCrBceBf/4Tzj7blOUJJ8CyZSpLEZEaRoV5JHbtMhN7br8d9u41z7FctgxOOsntZCIiEmZWn5K12g8/mCUjmZlmCciTT8Jdd8FvSzJERKRmUWEejnfegaFDIT+/7JIRERGpsXRK9lAUFcG998Kf/mTKskcPWLlSZSkiUgtohBmq3FwzkeeTT8z2XXfBE09AXd1rVUSkNlBhhuKLL8ySkY0bzZKRqVPh8svdTiUiIlVIp2QPxnFg4kRzynXjRmjb1syCVVmKiNQ6KswD2bkTBg82937du9eMMLVkRESk1tIp2Yp8/z1ccgmsWaMlIyIiAqgwy5szB667zjxRJD7e3Dj9rLPcTiUiIi7TKdkSRUXw17+amxEUFJiSXLlSZSkiIoBGmMaWLWbJSFqa2R41CsaP15IREREppcL8/HMzoefnnyEy0iwZGTTI7VQiImKZ2ntK1nHg//7PLBn5+Wc48UTIyFBZiohIhWpnYe7YYZaMjBhhrl1efrlZMtK2rdvJRETEUrXvlOzatWbJyH//C3XqwN//DnfeqSUjIiJyULWrMGfPhuuvN7NgmzUzS0Z69HA7lYiIVAO15pSs58EHzQOeCwrMdcuVK1WWIiISslpTmBHPP29+c889sHixuSmBiIhIiGrNKVknMhJeecVcvxQRETlEtaYwi//9bzj9dLdjiIhINVVrTsnSpo3bCUREpBqrPYUpIiJyBFSYIiIiIVBhioiIhECFKQD4/ZCTU/GxnBxzXESkNlNhCn4/9O1r7ueQ/bvSzM42+/v2VWmKSO1mfWEuWbKEAQMGkJCQgMfj4Z133nE7Uo1TUAC5uZCVBef1hvx8sz9nI6SkmP25ueZ1IiK1lfWFuXPnTjp16sTEiRPdjlJjJSaaZ2cnJ0PWOpg50+wfONCUZXKyOZ6Y6GZKERF3WX/jgn79+tGvX7+QXx8IBAgEAqXb+b8Nl4LBIMFgMOz5jkRJHhtyJSTAxx/DwIuC7NzpALB1a5B27YLMm2eOWxDTqs9sf7bmAnuz2ZoL7M1may6wN1s483gcx3HC9m6VzOPxMHfuXC6++OIDviY1NZUxY8aU25+WlkZkZGQlpqsZPM5e4gunArC53jAcT12XE4mIHL4dO3aQkpKC3+8nOjr6iN6rxhVmRSPMpKQktm7dSuPGjasgZeiCwSCZmZl06NABr9frdhxycqB/f4e8X/fw/nvf0PeCU4iLq8O8efacjrXtMythay6wN5utucDebLbmAnuz5eXlERsbG5bCtP6U7KHy+Xz4fL5y+71er1U/xP3ZkC07G3r1Mtcs27VrgOOpS1xcHdas8dKrl7mGmZTkasQybPjMKmJrLrA3m625wN5stuYC+7KFM4v1k36k8uXk7JsNm5wM8+aZ/fPm/TYRKMscP9A6TRGR2kCFKURFQVxc+dmw+8+ejYszrxMRqa2sPyW7Y8cOfvzxx9LtdevWsXr1ao4++mhatGjhYrKaIyYGFiww6ywTE8vOhk1KgvR0U5YxMe5lFBFxm/WFuXz5cs4555zS7VGjRgEwdOhQXn75ZZdS1TwxMQcuRFsm/IiIuMn6wkxJSaEaTeQVEZEaStcwRUREQqDCFBERCYEKU0REJAQqTBERkRCoMEVEREKgwhQREQmBClNERCQEKkwREZEQqDBFRERCoMIUEREJgQpTREQkBCpMERGREKgwRUREQqDCFBERCYEKU0REJAQqTBERkRCoMEVEREKgwhQREQmBClNERCQEKkwREZEQqDBFRERCoMIUEREJgQpTREQkBCpMERGREKgwRUREQqDCFBERCYEKU0REJATVojCff/55WrVqRf369encuTOffvqp25FERKSWsb4w33jjDUaOHMkDDzzAqlWrOOuss+jXrx8bNmxwO5qIiNQi1hfmhAkTuOGGG7jxxhs58cQTefbZZ0lKSmLSpEluRxMRkVqkjtsBDqawsJAVK1Zw7733ltnfp08fPv/88wq/JhAIEAgESrf9fj8A27dvr7SchysYDLJjxw7y8vLwer1uxyllay6wN5utucDebLbmAnuz2ZoL7M1W8m+/4zhH/F5WF+Yvv/xCMBikadOmZfY3bdqUzZs3V/g148aNY8yYMeX2JycnV0pGERGx37Zt24iJiTmi97C6MEt4PJ4y247jlNtX4r777mPUqFGl29u3b6dly5Zs2LDhiD+scMvPzycpKYns7Gyio6PdjlPK1lxgbzZbc4G92WzNBfZmszUX2JvN7/fTokULjj766CN+L6sL85hjjsHr9ZYbTebm5pYbdZbw+Xz4fL5y+2NiYqz6Ie4vOjraymy25gJ7s9maC+zNZmsusDebrbnA3mwREUc+ZcfqST/16tWjc+fOLFq0qMz+RYsW0a1bN5dSiYhIbWT1CBNg1KhRDBkyhC5dutC1a1emTJnChg0b+POf/+x2NBERqUWsL8wrrriCbdu28cgjj7Bp0ybat2/Phx9+SMuWLUP6ep/Px8MPP1zhaVq32ZrN1lxgbzZbc4G92WzNBfZmszUX2JstnLk8Tjjm2oqIiNRwVl/DFBERsYUKU0REJAQqTBERkRCoMEVEREJQowvT1seCLVmyhAEDBpCQkIDH4+Gdd95xOxJgbit42mmnERUVRVxcHBdffDFr1651OxaTJk2iY8eOpQuiu3btyvz5892OVc64cePweDyMHDnS7Sikpqbi8XjK/IqPj3c7VqmNGzdyzTXX0KRJExo2bMjJJ5/MihUrXM107LHHlvvMPB4Pw4cPdzUXQFFREQ8++CCtWrWiQYMGJCcn88gjj1BcXOx2NAoKChg5ciQtW7akQYMGdOvWjYyMjCrP8Uf/rjqOQ2pqKgkJCTRo0ICUlBTWrFlzSN+jxhamzY8F27lzJ506dWLixIluRykjPT2d4cOHs3TpUhYtWkRRURF9+vRh586druZKTExk/PjxLF++nOXLl9OrVy8GDhx4yH/ZK1NGRgZTpkyhY8eObkcp1a5dOzZt2lT6KzMz0+1IAOTl5dG9e3fq1q3L/Pnz+e9//8vTTz/NUUcd5WqujIyMMp9XyQ1TBg0a5GougCeeeILJkyczceJEvv32W5588kmeeuopnnvuObejceONN7Jo0SKmT59OZmYmffr0oXfv3mzcuLFKc/zRv6tPPvkkEyZMYOLEiWRkZBAfH895551HQUFB6N/EqaFOP/10589//nOZfW3btnXuvfdelxJVDHDmzp3rdowK5ebmOoCTnp7udpRyGjdu7Lz44otux3Acx3EKCgqcNm3aOIsWLXJ69uzpjBgxwu1IzsMPP+x06tTJ7RgVGj16tNOjRw+3Y/yhESNGOK1bt3aKi4vdjuL079/fGTZsWJl9l1xyiXPNNde4lMjYtWuX4/V6nQ8++KDM/k6dOjkPPPCAS6nK/7taXFzsxMfHO+PHjy/dt2fPHicmJsaZPHlyyO9bI0eYJY8F69OnT5n9B3ssmJRX8mi0cNy0OFyCwSCzZs1i586ddO3a1e04AAwfPpz+/fvTu3dvt6OU8cMPP5CQkECrVq248sorycrKcjsSAO+99x5dunRh0KBBxMXFccopp/Cvf/3L7VhlFBYWMmPGDIYNG3bABz1UpR49evDvf/+b77//HoCvvvqKzz77jAsuuMDVXEVFRQSDQerXr19mf4MGDfjss89cSlXeunXr2Lx5c5lO8Pl89OzZ85A6wfo7/RyOw3ksmJTlOA6jRo2iR48etG/f3u04ZGZm0rVrV/bs2UNkZCRz587lpJNOcjsWs2bNYuXKla5cszmYM844g1dffZXjjz+eLVu28Nhjj9GtWzfWrFlDkyZNXM2WlZXFpEmTGDVqFPfffz/Lli3jzjvvxOfzce2117qarcQ777zD9u3bue6669yOAsDo0aPx+/20bdsWr9dLMBjk8ccf56qrrnI1V1RUFF27duXRRx/lxBNPpGnTprz++ut8+eWXtGnTxtVs+yv5d7+iTli/fn3I71MjC7PEoTwWTMq6/fbb+frrr635r8QTTjiB1atXs337dmbPns3QoUNJT093tTSzs7MZMWIECxcuLPdf2G7r169f6e87dOhA165dad26Na+88kqZx9+5obi4mC5dujB27FgATjnlFNasWcOkSZOsKcyXXnqJfv36kZCQ4HYUwMzJmDFjBjNnzqRdu3asXr2akSNHkpCQwNChQ13NNn36dIYNG0bz5s3xer2ceuqpXH311axcudLVXBU50k6okYV5OI8Fk33uuOMO3nvvPZYsWUJiYqLbcQDz5JrjjjsOgC5dupCRkcE//vEPXnjhBdcyrVixgtzcXDp37ly6LxgMsmTJEiZOnEggELDmyfONGjWiQ4cO/PDDD25HoVmzZuX+Q+fEE09k9uzZLiUqa/369SxevJg5c+a4HaXUPffcw7333suVV14JmP8IWr9+PePGjXO9MFu3bk16ejo7d+4kPz+fZs2accUVV9CqVStXc+2vZIb45s2badasWen+Q+2EGnkNU48FOzyO43D77bczZ84cPv74Y6v+wv+e4zgEAgFXM5x77rlkZmayevXq0l9dunRh8ODBrF692pqyBAgEAnz77bdl/rFwS/fu3cstV/r+++9DfqBCZZs2bRpxcXH079/f7Sildu3aVe55jl6v14plJSUaNWpEs2bNyMvL46OPPmLgwIFuRyrVqlUr4uPjy3RCYWEh6enph9YJYZqUZJ1Zs2Y5devWdV566SXnv//9rzNy5EinUaNGzk8//eR2NKegoMBZtWqVs2rVKgdwJkyY4KxatcpZv369q7luvfVWJyYmxklLS3M2bdpU+mvXrl2u5rrvvvucJUuWOOvWrXO+/vpr5/7773ciIiKchQsXupqrIrbMkv3LX/7ipKWlOVlZWc7SpUudCy+80ImKirLi7/+yZcucOnXqOI8//rjzww8/OK+99prTsGFDZ8aMGW5Hc4LBoNOiRQtn9OjRbkcpY+jQoU7z5s2dDz74wFm3bp0zZ84c55hjjnH++te/uh3NWbBggTN//nwnKyvLWbhwodOpUyfn9NNPdwoLC6s0xx/9uzp+/HgnJibGmTNnjpOZmelcddVVTrNmzZz8/PyQv0eNLUzHcZx//vOfTsuWLZ169eo5p556qjXLIz755BMHKPdr6NChruaqKBPgTJs2zdVcw4YNK/05xsbGOueee66VZek49hTmFVdc4TRr1sypW7euk5CQ4FxyySXOmjVr3I5V6v3333fat2/v+Hw+p23bts6UKVPcjuQ4juN89NFHDuCsXbvW7Shl5OfnOyNGjHBatGjh1K9f30lOTnYeeOABJxAIuB3NeeONN5zk5GSnXr16Tnx8vDN8+HBn+/btVZ7jj/5dLS4udh5++GEnPj7e8fl8ztlnn+1kZmYe0vfQ471ERERCUCOvYYqIiISbClNERCQEKkwREZEQqDBFRERCoMIUEREJgQpTREQkBCpMERGREKgwRUREQqDCFBERCYEKU0REJAQqTBERkRCoMEVqoK1btxIfH1/6kGaAL7/8knr16rFw4UIXk4lUX7r5ukgN9eGHH3LxxRfz+eef07ZtW0455RT69+/Ps88+63Y0kWpJhSlSgw0fPpzFixdz2mmn8dVXX5GRkUH9+vXdjiVSLakwRWqw3bt30759e7Kzs1m+fDkdO3Z0O5JItaVrmCI1WFZWFj///DPFxcWsX7/e7Tgi1ZpGmCI1VGFhIaeffjonn3wybdu2ZcKECWRmZtK0aVO3o4lUSypMkRrqnnvu4e233+arr74iMjKSc845h6ioKD744AO3o4lUSzolK1IDpaWl8eyzzzJ9+nSio6OJiIhg+vTpfPbZZ0yaNMnteCLVkkaYIiIiIdAIU0REJAQqTBERkRCoMEVEREKgwhQREQmBClNERCQEKkwREZEQqDBFRERCoMIUEREJgQpTREQkBCpMERGREKgwRUREQvD/3FIZcRVDgesAAAAASUVORK5CYII=",
      "text/plain": [
       "<Figure size 500x500 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "fig, ax = plt.subplots(figsize=(5,5))\n",
    "\n",
    "# 绘制样本数据\n",
    "ax.scatter(x, y, marker = 'x', color = 'b')\n",
    "\n",
    "# 绘制回归直线\n",
    "ax.plot(x_array, y_array_pred, color='r')\n",
    "\n",
    "# 绘制预测值\n",
    "ax.scatter(x, y_pred, marker = 'x', color='r')\n",
    "\n",
    "# 绘制误差\n",
    "ax.plot(([i for i in x.squeeze()], [i for i in x.squeeze()]),\n",
    "        ([j for j in y_pred.squeeze()], [j for j in y.squeeze()]),\n",
    "         c='#FFC000', alpha = 0.5)\n",
    "\n",
    "# 装饰\n",
    "ax.set_xlabel('x')\n",
    "ax.set_ylabel('y')\n",
    "ax.set_xlim(0,10)\n",
    "ax.set_ylim(0,10)\n",
    "ax.set_xticks(np.arange(11))\n",
    "ax.set_yticks(np.arange(11))\n",
    "ax.grid(True, c = '0.8')\n",
    "ax.set_aspect('equal', 'box')"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "070c3389-8048-43a3-baa7-6666009bce96",
   "metadata": {},
   "source": [
    "作者\t**生姜DrGinger**  \n",
    "脚本\t**生姜DrGinger**  \n",
    "视频\t**崔崔CuiCui**  \n",
    "开源资源\t[**GitHub**](https://github.com/Visualize-ML)  \n",
    "平台\t[**油管**](https://www.youtube.com/@DrGinger_Jiang)\t\t\n",
    "\t\t[**iris小课堂**](https://space.bilibili.com/3546865719052873)\t\t\n",
    "\t\t[**生姜DrGinger**](https://space.bilibili.com/513194466)  "
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python [conda env:base] *",
   "language": "python",
   "name": "conda-base-py"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.12.7"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
